package com.netmi.mykotlinlearning.grammar

import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import com.netmi.mykotlinlearning.Person
import kotlin.reflect.KProperty

/**
 * 类描述：
 * 创建人：tgw
 * 创建时间：2020/8/11
 * 修改备注：
 */

class Example {
    var p: String by Delegate()

    var q: String = ""
        get() = field
        set(value) {
            field = value
        }

    lateinit var name: String
}


class Delegate {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, 这里委托了 ${property.name} 属性"
    }

    operator fun setValue(example: Any?, property: KProperty<*>, i: String) {
        println("$example 的 ${property.name} 属性赋值为 $i")
    }


}

val lazyValue: String by lazy {
    println("computed!")     // 第一次调用输出，第二次调用不执行
    "Hello"
}

fun sum(a: Int, b: Int): Int {   // Int 参数，返回值 Int
    return a + b
}


fun getStringLength(obj: Any): Int? {  //加个问号代表 返回值可以为 null
    if (obj is String) {
        return obj.length
    }
    return null;
}

//区间
fun getAera(obj: Any) {  //加个问号代表 返回值可以为 null
    if (obj in 1..4) {
        println(" 在区间：-------------")
    } else {
        println(" 不在区间：-------------")

    }
}

//创建数组
fun setNums() {  //加个问号代表 返回值可以为 null
    var nums1 = arrayOf("1", 2, 3, Example())
    for (it in nums1) {
        println(" 数组：-------------${it.toString()}类型$it")
    }
    val b = Array(3, fun(i: Int): Int {
        return (i * 2)
    })
    val b1 = Array(3, { i -> (i * 2) })

    //匿名函数
    val sumLambda: (Int, Int) -> Int = { x, y -> x + y }
    var nums2 = Array(3, { sum(sumLambda(1, 2), 3) })
    for (it in nums2) {
        println(" 数组2：-------------${it.toString()}类型$it")
    }
}

//创建字符串
fun setString() {
    var text = "sfas" +
            "asf" +
            "sfa"
//    Kotlin 支持三个引号 """ 扩起来的字符串，支持多行字符串，比如：
    val text1 = """sfas
        |asf
        |safafas
        |这是转义${'$'}的到肉符号 --- 另一个字符串的值：$text
    """.trimMargin()// 前置空格删除了
    text = "22"
    println(" 字符串值：-------------$text-值2-$text1")
}

//When 表达式
fun userWhen(x: Int?) {
    when (x) {

        1 -> println("x == 1")
        2 -> println("x == 2")
        else -> { // 注意这个块
            println("x 不是 1 ，也不是 2")
        }
    }

    //可以把多个分支条件放在一起，用逗号分隔
    when (x) {
        0, 1 -> println("x == 0 or x == 1")
        else -> println("otherwise")
    }

//    我们也可以检测一个值在（in）或者不在（!in）一个区间或者集合中：

    when (x) {
        in 1..10 -> println("x is in the range")
//        in validNumbers -> print("x is valid  这个参数是错的")
        !in 10..20 -> println("x is outside the range")
        else -> println("none of the above")
    }

    //另一种可能性是检测一个值是（is）或者不是（!is）一个特定类型的值。注意： 由于智能转换，你可以访问该类型的方法和属性而无需 任何额外的检测。
    fun hasPrefix(x: Any) = when (x) {
        is String -> x.startsWith("prefix")
        else -> false
    }
    println("是否为prefix：${hasPrefix("prefix")}")


//    when 也可以用来取代 if-else if链。 如果不提供参数，所有的分支条件都是简单的布尔表达式，而当一个分支的条件为真时则执行该分支：

    //扩展
    fun Int.isOdd(): Boolean = true
    fun Int.isEven(): Boolean = false

    //let函数--
    x?.let {
        when {
            x.isOdd() -> println("x is odd")
            x.isEven() -> println("x is even")
            else -> println("x is funny")
        }
    }


//    when 中使用 in 运算符来判断集合内是否包含某实例：
    when {
        x in 1..4 -> println("juicy")
        x in 4 downTo 0 -> println("apple is fine too")
    }

    val items = setOf("apple", "banana", "kiwi")
    when {
        "orange" in items -> println("items-juicy")
        "apple" in items -> println("items-apple is fine too")
    }
}


fun onBindViewHolder(holder: Int, position: Int) {


    with(Example()) {

        println("items-apple is fine too$name")

    }
    Example()?.let {
//         /println("items-apple is fine too$name")

    }
//    also函数的结构实际上和let很像唯一的区别就是返回值的不一样，
//    let是以闭包的形式返回，返回函数体内最后一行的值，
//    如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身
    Example()?.also {
//         /println("items-apple is fine too$name")

    }
    Example()?.run {
        //let  与 with 结合体
        println("items-apple is fine too$name")
    }
    Example()?.apply {
        //let  与 with 结合体从结构上来看apply函数和run函数很像，
        // 唯一不同点就是它们各自返回的值不一样
        // run函数是以闭包形式返回最后一行代码的值，而apply函数的返回的是传入对象的本身。
        println("items-apple is fine too$name")
    }

}

class Runoob {
    var name: String = "tgw1"
    var url: String = "tgwurl"
    var city: String = "tgwcity"

}

/**如果主构造器没有任何注解，也没有任何可见度修饰符，那么constructor关键字可以省略。*/
class Runoob1 constructor(firstName: String) {
    var name: String = "tgw1"
    var url: String = "tgwurl"
    var city: String = "tgwcity"

    //次级构造函数
    constructor() : this("tgw")
    constructor(lastName: String, firstName: String) : this()

    //    主构造器中不能包含任何代码，初始化代码可以放在初始化代码段中，初始化代码段使用 init 关键字作为前缀
    init {
        println("FirstName is $firstName")
    }

    var allByDefault: Int? = null // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
        get() {
            return field  //Kotlin 中类不能有字段。提供了 Backing Fields(后端变量) 机制,备用字段使用field关键字声明,
            // field 关键词只能用于属性的访问器，如这个实例：
        }
        set(value) {
            field = 10
//            field = 10

        }
    var initialized = 1    // 类型为 Int, 默认实现了 getter 和 setter
    val simple: Int?       // 类型为 Int ，默认实现 getter ，但必须在构造函数中初始化
        get() {
            return 5
        }

    //    非空属性必须在定义的时候初始化,kotlin提供了一种可以延迟初始化的方案,使用 lateinit 关键字描述属性：
    lateinit var simpleString: String
    val inferredType = 1   // 类型为 Int 类型,默认实现 getter
}

/**抽象是面向对象编程的特征之一，类本身，或类中的部分成员，都可以声明为abstract的。抽象成员在类中不存在具体的实现。

注意：无需对抽象类或抽象成员标注open注解。*/
open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}

/**内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用，所以内部类可以访问外部类成员属性和成员函数。*/
class Outer {
    private val bar: Int = 1
    var v = "成员属性"

    /**嵌套内部类**/
    inner class Inner {
        fun foo() = bar  // 访问外部类成员
        fun innerTest() {
            var o = this@Outer //获取外部类的成员变量
            println("内部类可以引用外部类的成员，例如：" + o.v)
        }
    }
}

/**匿名内部类：-------------使用对象表达式来创建匿名内部类*/
class Test {
    var v = "成员属性"

    fun setInterFace(test: TestInterFace) {
        test.test()
    }
}

/**
 * 定义接口
 */
interface TestInterFace {
    fun test()
}


/**子类有主构造函数*/
class Student(lastName: String) : Person("lastname") {

    constructor() : this("lastname") {
    }

}

/**子类没有主构造函数*/
class Student1 : Person {

    override var lastName: String
        get() = super.lastName
        set(value) {}

    constructor() : super("lastname") {
    }

    constructor(ctx: String, attrs: String) : super(ctx, attrs) {
    }
}


/**如果一个类定义有一个伴生对象 ，你也可以为伴生对象定义扩展函数和属性。
伴生对象通过"类名."形式调用伴生对象，伴生对象声明的扩展函数，通过用类名限定符来调用：*/
class MyClass {
    companion object {}  // 将被称为 "Companion"
}

fun MyClass.Companion.foo() {
    println("伴随对象的扩展函数")
}

val MyClass.Companion.nono: Int
    get() = 10


/**扩展声明为成员
在一个类内部你可以为另一个类声明扩展。

在这个扩展中，有个多个隐含的接受者，其中扩展方法定义所在类的实例称为分发接受者，而扩展方法的目标类型的实例称为扩展接受者。*/
class D {
    fun bar() {
        println("D bar")
    }
}

class C {
    fun baz() {
        println("C baz")
    }

    fun bar() {
        println("C bar")
    }

    /*在 C 类内，创建了 D 类的扩展,调用d类的bar（）方法 ，还调用c类的baz（）方法*/
    fun D.foo() {
        bar()   // 调用 D.bar  //优先调用d类的bar
        baz()   // 调用 C.baz
        this@C.bar() //调用  C类的bar
    }

    fun caller(d: D) {
        d.foo()   // 调用扩展函数
    }
}


/** 复制
复制使用 copy() 函数，我们可以使用该函数复制对象并修改部分属性, 对于上文的 User 类，其实现会类似下面这样：*/
data class User(val name: String, val age: Int)


/**密封类
密封类用来表示受限的类继承结构：当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上，
他们是枚举类的扩展：枚举类型的值集合 也是受限的，但每个枚举常量只存在一个实例，而密封类 的一个子类可以有可包含状态的多个实例。
声明一个密封类，使用 sealed 修饰类，密封类可以有子类，但是所有的子类都必须要内嵌在密封类中。
sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)*/
sealed class Expr
data class Const(val number: Double) : Expr()
class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr() {
    var num = 2.35
}

fun eval(expr: Expr): Double = when (expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}


/**定义泛型类型变量，可以完整地写明类型参数，如果编译器可以自动推定类型参*/
class Box<T>(t: T) {
    var value = t

}

fun <T> boxIn(value: T): Box<T> {
    when (value) {
        is Int -> println("整型数字为 $value")
        is String -> println("字符串转换为大写：${value.toUpperCase()}")
        else -> println("T 不是整型，也不是字符串")
    }
    return Box(value)
}

/**泛型约束
我们可以使用泛型约束来设定一个给定参数允许使用的类型。
Kotlin 中使用 : 对泛型的类型上限进行约束。
最常见的约束是上界(upper bound)：*/
/**默认的上界是 Any?。
对于多个上界约束条件，可以用 where 子句：*/
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
        where T : CharSequence,
              T : Comparable<T> {
    return list.filter(fun(it: T): Boolean {
        return it > threshold
    }).map(fun(it: T): String {
        return it.toString()
    })
}


/**有些时候, 你可能想表示你并不知道类型参数的任何信息, 但是仍然希望能够安全地使用它. 这里所谓"安全地使用"是指,
 * 对泛型类型定义一个类型投射, 要求这个泛型类型的所有的实体实例, 都是这个投射的子类型。

对于这个问题, Kotlin 提供了一种语法, 称为 星号投射(star-projection):
假如类型定义为 Foo<out T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,Foo<> 等价于 Foo<out TUpper> .
它表示, 当 T 未知时, 你可以安全地从 Foo<> 中 读取TUpper 类型的值.
假如类型定义为 Foo<in T> , 其中 T 是一个反向协变的类型参数, Foo<> 等价于 Foo<inNothing> .
它表示, 当 T 未知时, 你不能安全地向 Foo<> 写入 任何东西.
假如类型定义为 Foo<T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,
对于读取值的场合, Foo<*> 等价于 Foo<out TUpper> , 对于写入值的场合, 等价于 Foo<in Nothing> .
如果一个泛型类型中存在多个类型参数, 那么每个类型参数都可以单独的投射.
比如, 如果类型定义为interface Function<in T, out U> , 那么可以出现以下几种星号投射:
Function<*, String> , 代表 Function<in Nothing, String> ;
Function<Int, *> , 代表 Function<Int, out Any?> ;
Function<, > , 代表 Function<in Nothing, out Any?> .
注意: 星号投射与 Java 的原生类型(raw type)非常类似, 但可以安全使用*/
class A<T>(val t: T, val t2 : T, val t3 : T)
class Apple(var name : String)


fun main(args: Array<String>) {
    val e = Example()
    e.q = "委托赋值1"
    println(e.p)     // 访问该属性，调用 getValue() 函数
    e.name = "姓名"

    e.p = e.name    // 调用 setValue() 函数
    println(e.p)
    println("标准委托---延迟属性 Lazy")
    println(lazyValue)   // 第一次执行，执行两次输出表达式
    println(lazyValue)   // 第二次执行，只输出返回值 hello
    println(" sum 方法：-------------")
    println(" 区间：-------------")
    getAera(4);

    println(" 数组定义：-------------")
    setNums()
    println(" 字符串定义：-------------")
    setString()
    println(" When 表达式：-------------")
    userWhen(1)
    println(" 类定义：-------------")
    var r = Runoob().name;
    val r1 = Runoob1()
    r1.allByDefault = 1   //因为上面的set 方法我设置值 固定 为 10   所以该赋值无效
    println(" 类定义：-------------${r1.allByDefault}----${Runoob1().simple}")
    println(" 抽象类类定义：-------------kotlin中的类默认是final的不可继承，提供open 注解使其可以继承 ，其中的方法也一样")

    println(" 内部类类定义：-------------内部类使用 inner 关键字来表示。内部类会带有一个对外部类的对象的引用，所以内部类可以访问外部类成员属性和成员函数。")
    println(" 内部类定义：-------------${Outer().Inner().innerTest()}")


    println(" 匿名内部类：-------------使用对象表达式来创建匿名内部类：")
    var test = Test()
    /**
     * 采用对象表达式来创建接口对象，即匿名内部类的实例。
     */
    test.setInterFace(object : TestInterFace {
        override fun test() {
            println("对象表达式创建匿名内部类的实例")
        }
    })

    println("扩展函数之伴生对象：-------------：")
    println("no:${MyClass.nono}")
    MyClass.foo()

    println("扩展声明为成员：-------------：")
    val c: C = C()
    val d: D = D()
    c.caller(d)  //调用扩展函数

    println("Kotlin 数据类与密封类：-------------：复制使用 copy() 函数，我们可以使用该函数复制对象并修改部分属性, 对于上文的 User 类，其实现会类似下面这样：")
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2)
    println(jack)
    println(olderJack)

    println("Kotlin 数据类与密封类：-------------：数据类以及解构声明-组件函数允许数据类在解构声明中使用：")
    val jane = User("Jane", 35)
    val (name, age) = jane
    println("$name, $age years of age") // prints "Jane, 35 years of age"

    println("Kotlin 数据类与密封类：-------------密封类")
    println(eval(Const(1.00)))
    println(eval(Sum(Const(2.20), Const(3.50))))
    val notNum = NotANumber
    notNum.num = 2.35
    println(eval(notNum))


    println("Kotlin 泛型：-------------泛型")
    // 以下都是合法语句
    val box4 = boxIn<Int>(1)
    val box5 = boxIn(1)     // 编译器会进行类型推断
    val box6 = boxIn<String>("xiaozifu床")     // 编译器会进行类型推断
    println("Kotlin 泛型：-------------box4:${box4.value}=box5=${box5.value}box6：${box6.value}")


    println("List 操作符---reduce：-------------")
    val list = listOf(2, 3, 5)
    println(list.reduce(fun(ret: Int, i: Int): Int {
        println("$ret---$i")
        return ret+i
    }))//第一个参数ret是结果，第二个参数是代表要执行的操作，
    // 比如我这里是数组每个元素相乘，最后吧结果输出给ret。。。。下面我将进行加法操作，如下
    //输出：30
    //加法操作
    println(list.reduce { ret, i -> ret + i })
    //输出：10

    println("泛型指星号投射---：-------------")
//    关于星号投射，其实就是*代指了所有类型，相当于Any?
//    给文中补个例子方便理解：
//使用类
    val a1: A<*> = A(12, "String", Apple("苹果"))
    val a2: A<Any?> = A(12, "String", Apple("苹果"))   //和a1是一样的
    val apple = a1.t3    //参数类型为Any
    println(apple)
    val apple2 = apple as Apple   //强转成Apple类
    println(apple2.name)
    //使用数组
    val l:ArrayList<*> = arrayListOf("String",1,1.2f,Apple("苹果"))
    for (item in l){
        if (item is Apple){
            println(item.name)
        }
        println(item)
    }




    click.onClick()

    Singleton.Companion.getInstance(1)


    println("get 和set---：-------------")
   var setDemo =SetDemo()
    setDemo.test="测试看看1"
    println( setDemo.test)
}
val a = 1
object click {
     fun onClick() {
        val b = a + 1
         println("object使用---：-------------$b")
    }
}
class Singleton private constructor(private val param: Int) {
    companion object {
        @Volatile
        private var instance: Singleton? = null
        fun getInstance(property: Int) =
            instance ?: synchronized(this) {
                instance ?: Singleton(property).also { instance = it }
            }
    }
}

class SetDemo{
    var test = "测试看看"
    get():String{
        println("get--：------")
    return field
    }

    set(value : String){
        println("set---：-------------")
        field = "测试看看set"
    }
}


